import threading
from channels.generic.websocket import WebsocketConsumer
from threading import Thread
from asgiref.sync import async_to_sync
import socket
from django.conf import settings
from assets.models import ServerAssets
import django.utils.timezone as timezone
import json
from django.db.models import Q
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
from utils.tools import gen_rand_char
import time
import traceback
import platform
from ..guacamoleclient import Client
import os
import re
import base64
from django.http.request import QueryDict

# 异步保存终端日志 celery_save_terminal_log
# 文件保存终端日志 celery_save_res_asciinema
# session 如果在此期间未做任何操作，则退出， django 本身要么设置固定时间，要么关闭浏览器失效
terminal_exipry_time = 60 * 30  # 30分钟


class WebGuacamole(WebsocketConsumer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        query_string = self.scope.get('query_string').decode()
        guacamole_args = QueryDict(query_string=query_string, encoding='utf-8')
        self.host_id = int(guacamole_args.get('host_id'))
        self.remote_host = None
        self.width = guacamole_args.get('width')
        self.height = guacamole_args.get('height')
        self.dpi = guacamole_args.get('dpi')
        self.session = None
        self.start_time = timezone.now()
        self.send_flag = 0  # 0 发送自身通道，1 发送 group 通道，作用为当管理员查看会话时，进入 group 通道
        self.group = 'session_' + gen_rand_char()
        self.user_agent = None
        self.guacamoleclient = None
        self.lock = False
        self.last_operation_time = time.time()
        self.closed = False

    def connect(self):
        self.accept('guacamole')
        async_to_sync(self.channel_layer.group_add)(self.group, self.channel_name)  # 加入组
        self.session = self.scope.get('session', None)
        self.remote_host = ServerAssets.objects.get(id=self.host_id)
        _type = 7  # rdp类型
        self.guacamoleclient = Client(websocker=self)
        print(self.remote_host.ip, self.remote_host.port, self.remote_host.username, self.remote_host.password)
        self.guacamoleclient.connect(
            # protocol=self.remote_host.get_protocol_display(),
            protocol='rdp',
            hostname=self.remote_host.ip,
            port=self.remote_host.port,
            username=self.remote_host.username,
            password=self.remote_host.password,
            width=self.width,
            height=self.height,
            dpi=self.dpi,
        )
        for i in self.scope['headers']:
            if i[0].decode('utf-8') == 'user-agent':
                self.user_agent = i[1].decode('utf-8')
                break
        # 连接记录
        t = threading.Thread(target=self.check_timeout)
        t.daemon = True
        t.start()

    def disconnect(self, close_code):
        time.sleep(0.5)
        if not self.closed:
            self.closed = True
            try:
                async_to_sync(self.channel_layer.group_discard)(self.group, self.channel_name)
                if close_code == 3001:
                    pass
                else:
                    self.guacamoleclient.close()
            except Exception:
                pass

    def receive(self, text_data=None, bytes_data=None):
        if not self.lock:
            self.guacamoleclient.shell(text_data)
            if not text_data.startswith('4.sync') and not text_data.startswith('3.nop'):
                self.last_operation_time = time.time()
        else:
            if text_data.startswith('4.sync') or text_data.startswith('3.nop'):
                self.guacamoleclient.shell(text_data)
            else:
                if re.match(r'^5\.mouse,.*1\.1;$', text_data) or re.match(r'^3\.key,.*1\.1;$', text_data):
                    message = str(base64.b64encode('当前会话已被管理员锁定'.encode('utf-8')), 'utf-8')
                    self.send('6.toastr,1.1,{0}.{1};'.format(len(message), message))  # 给客户端发送警告

    # 会话外使用 channels.layers 设置 type 为 group.message 调用此函数
    def group_message(self, data):
        try:
            self.send(data['text'])
        except Exception:
            pass

    # 会话外使用 channels.layers 设置 type 为 close.message 调用此函数
    def close_message(self, data):
        try:
            message = str(base64.b64encode('当前会话已被管理员关闭'.encode('utf-8')), 'utf-8')
            # 给客户端发送toastr警告
            # 需要在 guacamole/js/all.js 中自定义 toastr 的处理处理方法
            self.send('6.toastr,1.2,{0}.{1};'.format(len(message), message))
            self.close()
        except Exception:
            pass

    def lock_message(self, data):
        if not self.lock:
            self.lock = True
            message = str(base64.b64encode('当前会话已被管理员锁定'.encode('utf-8')), 'utf-8')
            self.send('6.toastr,1.1,{0}.{1};'.format(len(message), message))

    def unlock_message(self, data):
        if self.lock:
            self.lock = False
            message = str(base64.b64encode('当前会话已被管理员解锁'.encode('utf-8')), 'utf-8')
            self.send('6.toastr,1.0,{0}.{1};'.format(len(message), message))

    def check_timeout(self, sleep_time=3):
        while 1:
            if self.closed:
                break

            if int(time.time() - self.last_operation_time) >= terminal_exipry_time:
                try:
                    message = str(base64.b64encode('由于长时间没有操作或者没有数据返回，连接已断开!'.encode('utf-8')), 'utf-8')
                    self.send('6.toastr,1.2,{0}.{1};'.format(len(message), message))
                    self.close()
                except Exception:
                    pass
                break

            time.sleep(sleep_time)
